package com.kanavi.dataAccess.config;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.kanavi.dataAccess.entity.SysDataScope;
import com.kanavi.dataAccess.service.SysDataScopeService;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.*;
import net.sf.jsqlparser.util.TablesNamesFinder;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

@Component
@Slf4j
public class ApiAuthInterceptor implements InnerInterceptor {

  @Autowired private SysDataScopeService dataScopeService;

  @Override
  public void beforeQuery(
      Executor executor,
      MappedStatement ms,
      Object parameter,
      RowBounds rowBounds,
      ResultHandler resultHandler,
      BoundSql boundSql)
      throws SQLException {
    ApiAuthEntity authEntity = ApiAuthContextHolder.getAuthEntity();
    ApiAuthContextHolder.clear();
    if (authEntity != null && authEntity.getIsAuth()) {
      Map<String, Object> params = authEntity.getParams();
      AbstractApiAuthParamsParser paramsParser = authEntity.getParamsParser();
      PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
      String originSql = mpBoundSql.sql();
      try {
        Statement parse = CCJSqlParserUtil.parse(originSql);
        if (parse instanceof Select) {
          Select select = (Select) parse;
          TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();
          List<String> tableList = tablesNamesFinder.getTableList(select);
          // todo 获取用户权限
          List<SysDataScope> dataScopeList = dataScopeService.list();
          List<String> userAuthTables = new ArrayList<>();
          for (SysDataScope dataScope : dataScopeList) {
            userAuthTables.add(dataScope.getTableName());
          }
          if (userAuthTables.containsAll(tableList)) {
            for (SysDataScope dataScope : dataScopeList) {
              if (tableList.contains(dataScope.getTableName())) {
                if (dataScope.getType() != 3) {
                  this.processSelect(select, dataScope, params, paramsParser);
                } else if (dataScope.getType() == 3) {
                  this.processField(select, dataScope);
                }
              }
            }
            String authSql = parse.toString();
            log.info(authSql + "::: 执行 sql！");
            mpBoundSql.sql(authSql);
          } else {
            this.setNoAuthExpression(select);
            String noAuthSql = parse.toString();
            log.info(noAuthSql + "::: 执行 sql！");
            mpBoundSql.sql(noAuthSql);
          }
        }
      } catch (JSQLParserException e) {
        e.printStackTrace();
      }
    }
  }

  /**
   * 没有当前表的权限
   *
   * @param select
   * @throws JSQLParserException
   */
  protected void setNoAuthExpression(Select select) throws JSQLParserException {
    SelectBody selectBody = select.getSelectBody();
    if (selectBody instanceof PlainSelect) {
      PlainSelect plainSelect = (PlainSelect) selectBody;
      String sqlPart = "1 = 2";
      Expression expression = CCJSqlParserUtil.parseCondExpression(sqlPart);
      Expression where = plainSelect.getWhere();
      if (where == null) {
        plainSelect.setWhere(new Parenthesis(expression));
      } else {
        plainSelect.setWhere(new AndExpression(plainSelect.getWhere(), expression));
      }
    }
  }

  /**
   * 处理能产看字段
   *
   * @param select
   * @param dataScope
   */
  protected void processField(Select select, SysDataScope dataScope) {
    SelectBody selectBody = select.getSelectBody();
    if (selectBody instanceof PlainSelect) {
      List<SelectItem> selectItemList = new ArrayList<>();
      List<String> fields = new ArrayList<>(Arrays.asList(dataScope.getExpression().split(",")));
      List<String> fieldsTrim = new ArrayList<>();
      for (String s : fields) {
        String temp = s.trim();
        fieldsTrim.add(temp);
      }
      for (SelectItem selectItem : ((PlainSelect) selectBody).getSelectItems()) {
        if (fieldsTrim.contains(selectItem.toString())) {
          selectItemList.add(selectItem);
        }
      }
      if (selectItemList.size() > 0) {
        ((PlainSelect) selectBody).setSelectItems(selectItemList);
      }
    }
  }

  protected void processSelect(
      Select select,
      SysDataScope dataScope,
      Map<String, Object> params,
      AbstractApiAuthParamsParser paramsParser)
      throws JSQLParserException {
    processSelectBody(select.getSelectBody(), dataScope, params, paramsParser);
    List<WithItem> withItemsList = select.getWithItemsList();
    if (!CollectionUtils.isEmpty(withItemsList)) {
      for (WithItem withItem : withItemsList) {
        this.processSelect(select, dataScope, params, paramsParser);
      }
    }
  }

  protected void processSelectBody(
      SelectBody selectBody,
      SysDataScope dataScope,
      Map<String, Object> params,
      AbstractApiAuthParamsParser paramsParser)
      throws JSQLParserException {
    if (selectBody == null) {
      return;
    }
    if (selectBody instanceof PlainSelect) {
      processPlainSelect((PlainSelect) selectBody, dataScope, params, paramsParser);
    } else if (selectBody instanceof WithItem) {
      WithItem withItem = (WithItem) selectBody;
      processSelectBody(withItem.getSelectBody(), dataScope, params, paramsParser);
    } else {
      SetOperationList operationList = (SetOperationList) selectBody;
      List<SelectBody> selectBodyList = operationList.getSelects();
      if (CollectionUtils.isNotEmpty(selectBodyList)) {
        for (SelectBody body : selectBodyList) {
          this.processSelectBody(selectBody, dataScope, params, paramsParser);
        }
      }
    }
  }

  protected void processPlainSelect(
      PlainSelect plainSelect,
      SysDataScope dataScope,
      Map<String, Object> params,
      AbstractApiAuthParamsParser paramsParser)
      throws JSQLParserException {
    String sqlPart = paramsParser.parse(plainSelect, dataScope, params);
    Expression expression = CCJSqlParserUtil.parseCondExpression(sqlPart);
    Expression where = plainSelect.getWhere();
    if (where == null) {
      plainSelect.setWhere(new Parenthesis(expression));
    } else if (dataScope.getType().equals(1)) {
      plainSelect.setWhere(new AndExpression(plainSelect.getWhere(), expression));
    } else if (dataScope.getType().equals(2)) {
      plainSelect.setWhere(new OrExpression(plainSelect.getWhere(), expression));
    }
  }
}
